package com.niiwoo.civet.trade.service.local.loan;

import com.alibaba.fastjson.JSON;
import com.niiwoo.civet.base.enums.ChannelCodeEnum;
import com.niiwoo.civet.trade.constant.TradeRedisKey;
import com.niiwoo.civet.trade.dao.entity.LoanTimeConfig;
import com.niiwoo.civet.trade.dao.entity.Project;
import com.niiwoo.civet.trade.dao.entity.ProjectDeliveryChannel;
import com.niiwoo.civet.trade.dao.entity.ProjectDeliveryChannelRecord;
import com.niiwoo.civet.trade.dao.mapper.LoanTimeConfigMapper;
import com.niiwoo.civet.trade.dao.mapper.ProjectDeliveryChannelMapperExt;
import com.niiwoo.civet.trade.dao.mapper.ProjectDeliveryChannelRecordMapperExt;
import com.niiwoo.civet.trade.dao.mapper.ProjectInvestingMapperExt;
import com.niiwoo.civet.trade.enums.TradeConfigEnum;
import com.niiwoo.civet.trade.service.local.configure.TradeConfigLocalService;
import com.niiwoo.tripod.provider.exception.BizException;
import com.niiwoo.tripod.service.component.SnowflakeIdWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 分端、分时发标服务类
 */
@Slf4j
@Service
public class SpeedLoanDispenseChannelService {
    @Autowired
    private LoanTimeConfigMapper loanTimeConfigMapper;
    @Autowired
    private TradeConfigLocalService tradeConfigService;
    @Autowired
    private ProjectDeliveryChannelMapperExt projectDeliveryChannelMapperExt;
    @Autowired
    private ProjectDeliveryChannelRecordMapperExt projectDeliveryChannelRecordMapperExt;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    private static final String TOTAL_WEIGHT = "TOTAL";
    private static final String CHANNEL_CODE_APP = "APP";
    private static final String deliveryChannelCount = "DeliveryChannelCount";
    private static final String deliveryChannelLock = "deliveryChannelLock";
    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;
    @Autowired
    private ProjectInvestingMapperExt projectInvestingMapperExt;

    @Transactional(isolation = Isolation.READ_COMMITTED)
    public String getChannelCode(Project project) {
        String bestChannelCode = null;
        Long bestDeliveryChannelId = null;
        Long deliveryRuleId=null;
        Date today = new Date();

        //当前是否有标的正在进行分端
        Boolean isAllotChannelLock = redisTemplate.opsForValue().setIfAbsent(TradeRedisKey.ALLOT_CHANNEL_LOCK, System.currentTimeMillis() + "");
        if (isAllotChannelLock) {
            redisTemplate.expire(TradeRedisKey.ALLOT_CHANNEL_LOCK, 5, TimeUnit.MINUTES);
        }
        //若有标的正在进行分端,则暂时不处理该标的,等待正在进行分端的标完成后再分端;
        else {
            log.error("标的分端,标的ID={},目前正在进行标的分端操作.",project.getProjectId());
            throw new BizException("TRD10027");
        }

        try {
            //判断分端开关状态(0:关闭;1:开启)
            String projectDeliveryChannelStatus = tradeConfigService.selectTradeConfigByEmun(TradeConfigEnum.PROJECTDELIVERYCHANNELSTATUS);
            log.info("标的分端,标的ID={},当前分端开关状态={}",project.getProjectId(),projectDeliveryChannelStatus);

            //若分端开关开启,则进行分端操作
            if ((!StringUtils.isEmpty(projectDeliveryChannelStatus)) && "1".equals(projectDeliveryChannelStatus)) {
                String currTime = DateFormatUtils.format(today, "HH:mm");
                // 2、查询借款期限和利率都符合的渠道
                // 2.1 渠道没有配置任何规则且是有效状态
                // 2.2 当前项目匹配渠道借款期限和利率
                // 2.3 渠道优先级从高到低排序。1代表最高优先级
                Map<String, Object> condition = new HashMap<String, Object>();
                condition.put("deadlineUnit", project.getDeadlineUnit());
                condition.put("deadline", project.getDeadline());
                condition.put("interestRate", project.getBorrowRate());
                condition.put("beginTime", currTime);
                condition.put("endTime", currTime);
                List<Map<String, Object>> deliveryChannelList = projectDeliveryChannelMapperExt.selectByDeadLineAndInterestRate(condition);
                log.info("标的分端,标的ID={},分端筛选条件={},符合条件的分端列表={}",project.getProjectId(), JSON.toJSONString(condition),JSON.toJSONString(deliveryChannelList));

                //资产来源筛选
                Iterator<Map<String, Object>> iter = deliveryChannelList.iterator();
                while (iter.hasNext()) {
                    Map<String, Object> channelRule = iter.next();
                    String fromSource = channelRule.get("fromSource") == null ? "" : String.valueOf(channelRule.get("fromSource"));
                    if (org.apache.commons.lang3.StringUtils.isEmpty(fromSource)) {
                        iter.remove();
                        continue;
                    }

                    List<String> fromSourceList = Arrays.asList(fromSource.split(","));
                    if (!fromSourceList.contains(project.getOrgId())) {
                        iter.remove();
                    }

//                    if(channelRule.get("channelCode").equals(ChannelCodeEnum.XINSHOUBIAO.getChanelCode())&&hasXinShouBiaoInInvesting()){
//                        iter.remove();
//                    }
                }

                log.info("标的分端,标的ID={},资产来源筛选结束后,符合条件的分端列表={}",project.getProjectId(), JSON.toJSONString(deliveryChannelList));

                List<Long> channelIdList = new ArrayList<Long>();
                iter = deliveryChannelList.iterator();

                //剔除已达每日投放金额上限的渠道
                while (iter.hasNext()) {
                    Map<String, Object> channelRule = iter.next();
                    boolean isInRuleDeliveryAmount = ((SpeedLoanDispenseChannelService) AopContext.currentProxy()).isInChannelRuleDeliveryAmount(project,channelRule, today);
                    if (!isInRuleDeliveryAmount) {
                        iter.remove();
                    }

                    channelIdList.add((Long) channelRule.get("id"));
                }

                log.info("标的分端,标的ID={},投放金额筛选结束后,符合条件的分端列表={}",project.getProjectId(), JSON.toJSONString(deliveryChannelList));
                //投放金额已到达上限的渠道
                if (channelIdList.size() > 0) {
                    List<Map<String, Object>> channelStats = projectDeliveryChannelRecordMapperExt.selectByPriority(DateFormatUtils.format(today, com.niiwoo.tripod.base.utils.DateUtils.SHOW_DATE_FORMAT),channelIdList);
                    log.info("标的分端,标的ID={},统计今日各渠道投放的金额列表={}",project.getProjectId(), JSON.toJSONString(channelStats));

                    for (Map<String, Object> channel : deliveryChannelList) {
                        Long deliveryChannelId = (Long) channel.get("id");
                        //4.1、判断是否超过渠道日投放金额
                        boolean isInDailyAmountFlag = ((SpeedLoanDispenseChannelService)AopContext.currentProxy()).isInChannelDailyAmount(project,channel, channelStats);
                        log.info("标的分端,标的ID={},渠道ID={},是否已超过渠道每日投放金额={}",project.getProjectId(),deliveryChannelId, JSON.toJSONString(isInDailyAmountFlag));

                        //4.2、判断权重和循环
                        if (isInDailyAmountFlag) {
                            String channelCode = (String) channel.get("channelCode");
                            boolean flag = ((SpeedLoanDispenseChannelService)AopContext.currentProxy()).judgeCanDeliveryAndRefresh(project.getProjectId(),channelCode);
                            log.info("标的分端,标的ID={},渠道ID={},当前渠道是否可投放={}",project.getProjectId(),deliveryChannelId, JSON.toJSONString(flag));
                            if (flag) {
                                bestChannelCode = channelCode;
                                bestDeliveryChannelId = deliveryChannelId;
                                deliveryRuleId= (Long) channel.get("ruleId");
                                break;
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("标的分端,标的ID={},发生系统异常,原因={}", project.getProjectId(),e);
            bestChannelCode = null;
        }finally {
            //处理完成,释放锁;
            redisTemplate.delete(TradeRedisKey.ALLOT_CHANNEL_LOCK);
            log.info("标的分端,标的ID={},分端锁已释放", project.getProjectId());
        }

        // 插入投放记录 如果投放渠道为空，则查找默认渠道
        if (bestDeliveryChannelId == null) {
            ProjectDeliveryChannel channel = projectDeliveryChannelMapperExt.selectDefaultDeliveryChannel();
            log.info("标的分端,标的ID={},未找到合适渠道,发往默认渠道={}.",project.getProjectId(),channel.getChannelCode());
            bestDeliveryChannelId = channel.getId();
            bestChannelCode = channel.getChannelCode();
            decr(TOTAL_WEIGHT);
        }
        try {
            ((SpeedLoanDispenseChannelService) AopContext.currentProxy()).insertDeliveryRecord(bestDeliveryChannelId, deliveryRuleId, project, today);
        }catch (Exception e){
            log.error("标的分端,标的ID={},insertDeliveryRecord方法发生系统异常,原因={}", project.getProjectId(), e);
            throw e;
        }
        return bestChannelCode;
    }

    public boolean hasXinShouBiaoInInvesting() {
        return projectInvestingMapperExt.selectLastOneByChannelCode(ChannelCodeEnum.XINSHOUBIAO.getChanelCode())!=null;
    }

    /**
     * 判断当前渠道是否可以投放，如果可投，刷新计数器
     *
     * @param channelCode
     * @return
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public boolean judgeCanDeliveryAndRefresh(Long projectId, String channelCode) {
        Integer count = getValue(TOTAL_WEIGHT);
        log.info("标的ID={},渠道编码={},本轮循环剩余标的数={}", projectId, channelCode, count);
        if (count <= 0) {  // 此次循环已结束,重新初始化
            ((SpeedLoanDispenseChannelService)AopContext.currentProxy()).refreshChannel();
        }

        if (!StringUtils.isEmpty(channelCode)) {
            Integer channelWeight = getValue(channelCode);
            log.info("标的ID={},渠道编码={},该渠道本轮循环剩余标的数={}", projectId, channelCode, channelWeight);
            if (channelWeight == 0) { // 当前渠道在本次循环已满
                log.info("标的ID={},渠道编码={},当前渠道在本次循环已满.", projectId, channelCode);
                return false;
            }
            decr(channelCode);
        }
        decr(TOTAL_WEIGHT);
        return true;
    }

    /**
     * @param bestDeliveryChannelId
     * @param project
     * @param deliveryDate
     * @Description 插入投放记录
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void insertDeliveryRecord(Long bestDeliveryChannelId,Long deliveryRuleId,Project project, Date deliveryDate) {
        ProjectDeliveryChannelRecord record = new ProjectDeliveryChannelRecord();
        record.setId(snowflakeIdWorker.nextId());
        record.setProjectId(project.getProjectId());
        record.setDeliveryAmount(project.getContractAmount());
        record.setDeliveryChannelId(bestDeliveryChannelId);
        record.setDeliveryRuleId(deliveryRuleId);
        record.setDeliveryDate(deliveryDate);
        record.setCreateTime(new Date());

        projectDeliveryChannelRecordMapperExt.insertSelective(record);
    }

//    /**
//     * @return Date
//     * @Description 根据配置获取当前标的释放时间
//     */
//    public Date getProjectReleaseTimeByConfig() {
//        try {
//            Date now = new Date();
//            // 极速借 标的定时上线配置类型 0-特殊时段配置 1-递推固定配置
//            String projectTimeConfigType = tradeConfigService.selectTradeConfigByEmun(TradeConfigEnum.PROJECTONLINETYPE);
//            if (StringUtils.isEmpty(projectTimeConfigType)) {
//                projectTimeConfigType = "0";
//            }
//
//            if ("0".equals(projectTimeConfigType)) {
//                return getRealeaseTimeONE(now);
//            } else if ("1".equals(projectTimeConfigType)) {
//                return getRealeaseTimeTWO(now);
//            } else {
//                return null;
//            }
//        } catch (Exception e) {
//            log.error("获取标的定时上线时间错误" + e.getMessage());
//            return null;
//        }
//    }

//    /**
//     *
//     * @Description 获取极速借标的上线时间
//     * @param now
//     * @throws Exception
//     * @return Date
//     *
//     */
//    public Date getRealeaseTimeTWO(Date now) throws Exception{
//        List<LoanTimeConfig> timeConfigList = getTimeConfigList();
//        for(LoanTimeConfig timeConfig : timeConfigList){
//            Date startTime = timeConfig.getStartDate();
//            Date endTime = timeConfig.getEndDate();
//
//            if(now.compareTo(startTime) >= 0 && now.compareTo(endTime) <= 0){
//                return endTime;
//            }
//        }
//
//        return null;
//    }

//    /**
//     *
//     * @Description 根据规则取出时间段列表
//     * @param
//     * @return List<ProjectTimeConfig>
//     *
//     */
//    private List<LoanTimeConfig> getTimeConfigList() throws ParseException {
//        Integer timeRule = getSysConfigTime();
//
//        Date today = new Date();
//        String todayStartStr = getDateStart(today);
//        String todayEndStr = getDateEnd(today);
//        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//        Date todayStartDate = simpleDateFormat.parse(todayStartStr);
//        Date todayEndDate = simpleDateFormat.parse(todayEndStr);
//
//        List<LoanTimeConfig> timeConfigList = new ArrayList<LoanTimeConfig>();
//        while(todayStartDate.compareTo(todayEndDate) < 0){
//            LoanTimeConfig timeConfig = new LoanTimeConfig();
//            Date endDate = DateUtils.addMinutes(todayStartDate,timeRule);
//
//            timeConfig.setStartDate(todayStartDate);
//            timeConfig.setEndDate(endDate);
//            timeConfigList.add(timeConfig);
//
//            todayStartDate = endDate;
//        }
//
//        return timeConfigList;
//    }

//    /**
//     *
//     * @Description 获取指定日期开始时间
//     * @param today
//     * @return
//     * @return String
//     * @throws
//     *
//     */
//    public static String getDateStart(Date today) {
//        String todayFormat = new SimpleDateFormat("yyyy-MM-dd").format(today);
//        return todayFormat + " 00:00:00";
//    }
//
//    /**
//     *
//     * @Description 获取指定日期结束时间
//     * @param today
//     * @return
//     * @return String
//     * @throws
//     *
//     */
//    public static String getDateEnd(Date today) {
//        String todayFormat = new SimpleDateFormat("yyyy-MM-dd").format(today);
//        return todayFormat + " 23:59:59";
//    }

//    /**
//     *
//     * @Description 获取系统配置的递推时间配置
//     * @return Integer
//     *
//     */
//    private Integer getSysConfigTime(){
//        String projectTimeConfigIntervalStr = tradeConfigService.selectTradeConfigByEmun(TradeConfigEnum.PROJECTTIMECONFIGINTERVAL);
//        return (int)(Double.valueOf(projectTimeConfigIntervalStr) * 60);
//    }

//    /**
//     *
//     * @Description 获取极速借标的上线时间
//     * @param now
//     * @throws Exception
//     * @return Date
//     *
//     */
//    @Transactional(isolation = Isolation.READ_COMMITTED)
//    public Date getRealeaseTimeONE(Date now) throws Exception{
//
//        //解析时间
//        String yyyymmddStr = DateFormatUtils.format(now, "yyyy-MM-dd");
//        String hhmmStr = DateFormatUtils.format(now, "HH:mm");
//
//        Date yyyymmddDate = new SimpleDateFormat("yyyy-MM-dd").parse(yyyymmddStr);
//        Date hhmmDate = new SimpleDateFormat("yyyy-MM-dd").parse(hhmmStr);
//
//        List<LoanTimeConfig> configs = loanTimeConfigMapper.selectValidTimeConfigs();
//        for(LoanTimeConfig config : configs){
//            Date startDate = config.getStartDate();
//            Date endDate = config.getEndDate();
//            Date startTime = config.getStartTime();
//            Date endTime = config.getEndTime();
//
//            if(yyyymmddDate.compareTo(startDate) >= 0 && yyyymmddDate.compareTo(endDate) <= 0
//                    && hhmmDate.compareTo(startTime) >= 0 && hhmmDate.compareTo(endTime) <= 0){
//                String endTimeStr = DateFormatUtils.format(endTime, "HH:mm");
//                return (new SimpleDateFormat("yyyy-MM-dd HH:mm")).parse(yyyymmddStr + " " + endTimeStr);
//            }
//        }
//
//        return null;
//    }

    /**
     * @param channelRule
     * @param today
     * @return boolean
     * @Description 判断渠道规则是否超过最大投放金额
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public boolean isInChannelRuleDeliveryAmount(Project project,Map<String, Object> channelRule, Date today) {
        Long id = (Long) channelRule.get("id");
        BigDecimal maxDeliveryAmount = (BigDecimal) channelRule.get("maxDeliveryAmount");
        if(maxDeliveryAmount==null || maxDeliveryAmount.equals(BigDecimal.ZERO)){
            log.info("标的分端,标的ID={},渠道规则ID={},每日投放上限金额={}", project.getProjectId(), channelRule.get("ruleId"), maxDeliveryAmount);
            return false;
        }
        Integer deadlineUnit = (Integer) channelRule.get("deadlineUnit");
        Integer minDeadline = (Integer) channelRule.get("minDeadline");
        Integer maxDeadLine = (Integer) channelRule.get("maxDeadLine");
        Long ruleId = (Long) channelRule.get("ruleId");
        Map<String, Object> condition = new HashMap<String, Object>();
        condition.put("id", id);
        condition.put("deliveryDate", DateFormatUtils.format(today, com.niiwoo.tripod.base.utils.DateUtils.SHOW_DATE_FORMAT));
        condition.put("deadlineUnit", deadlineUnit);
        condition.put("minDeadline", minDeadline);
        condition.put("maxDeadLine", maxDeadLine);
        condition.put("ruleId", ruleId);
        BigDecimal deliveryAmount = projectDeliveryChannelRecordMapperExt.countDeliveryAmount(condition);
        log.info("标的分端,标的ID={},渠道规则ID={},每日投放上限金额={},当前已投放金额={}", project.getProjectId(), channelRule.get("ruleId"), maxDeliveryAmount, deliveryAmount);

        if (deliveryAmount == null || maxDeliveryAmount.compareTo(deliveryAmount) > 0) {
            return true;
        }
        return false;
    }

    /**
     * @param channel
     * @param channelStats
     * @return boolean
     * @Description 判断当前渠道是否超过日投放金额
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public boolean isInChannelDailyAmount(Project project,Map<String, Object> channel, List<Map<String, Object>> channelStats) {
        boolean flag = false;
        Long deliveryChannelId = (Long) channel.get("id");
        BigDecimal dailyMaxAmount = (BigDecimal) channel.get("dailyMaxAmount");
        if(dailyMaxAmount==null || dailyMaxAmount.equals(BigDecimal.ZERO)){
            log.info("标的分端,标的ID={},渠道ID={},当前每日投放金额上限={}",project.getProjectId(),deliveryChannelId, dailyMaxAmount);
            return false;
        }
        BigDecimal channelDeliveryAmount = null;
        // 当前渠道已投放金额
        for (Map<String, Object> channelStat : channelStats) {
            if (channelStat.get("deliveryChannelId").equals(deliveryChannelId)) {
                channelDeliveryAmount = (BigDecimal) channelStat.get("channelDeliveryAmount");
                break;
            }
        }

        log.info("标的分端,标的ID={},渠道ID={},当前每日投放金额上限={},已投放金额={}",project.getProjectId(),deliveryChannelId, dailyMaxAmount,channelDeliveryAmount);
        if (channelDeliveryAmount == null || (channelDeliveryAmount.compareTo(dailyMaxAmount) < 0)) {
            return true;
        }
        return flag;
    }

    private void decr(String channelCode) {
        redisTemplate.opsForHash().increment(deliveryChannelCount, channelCode, -1);
    }

    private Integer getValue(String key) {
        Object obj = redisTemplate.opsForHash().get(deliveryChannelCount, key);
        if (obj == null) {
            return 0;
        }
        return Integer.valueOf(obj.toString());
    }

    /**
     * @return void
     * @Description 刷新渠道数据
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void refreshChannel() {
        //当前是否有标的正在进行分端
        Boolean isAllotChannelLock = redisTemplate.opsForValue().setIfAbsent(TradeRedisKey.ALLOT_CHANNEL_CONFIG_REFRESH_LOCK, System.currentTimeMillis() + "");
        if (isAllotChannelLock) {
            redisTemplate.expire(TradeRedisKey.ALLOT_CHANNEL_CONFIG_REFRESH_LOCK, 5, TimeUnit.MINUTES);
        }
        //若有标的正在进行分端,则暂时不处理该标的,等待正在进行分端的标完成后再分端;
        else {
            log.error("分端配置刷新,目前正在进行配置刷新操作.");
            return;
        }

        try {
            redisTemplate.delete(deliveryChannelCount);
            List<ProjectDeliveryChannel> deliveryChannelList = projectDeliveryChannelMapperExt.selectEffectiveDeliveryChannelList();
            log.info("刷新渠道数据,当前获取的有效渠道列表={}", JSON.toJSONString(deliveryChannelList));
            int total = 0;
            for (ProjectDeliveryChannel deliveryChannel : deliveryChannelList) {
                total += deliveryChannel.getWeight();
                redisTemplate.opsForHash().increment(deliveryChannelCount, deliveryChannel.getChannelCode(), deliveryChannel.getWeight());
            }

            redisTemplate.opsForHash().increment(deliveryChannelCount, TOTAL_WEIGHT, total);
        }finally {
            //处理完成,释放锁;
            redisTemplate.delete(TradeRedisKey.ALLOT_CHANNEL_CONFIG_REFRESH_LOCK);
            log.info("分端配置刷新,配置刷新操作完成.");
        }
    }
}


